package org.kolonitsky.css.writers;

import org.w3c.css.sac.AttributeCondition;
import org.w3c.css.sac.CombinatorCondition;
import org.w3c.css.sac.Condition;
import org.w3c.css.sac.LangCondition;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import java.util.HashMap;
import java.util.Map;

import static org.w3c.css.sac.Condition.*;

/**
 * @author Alex.Kolonitsky
 */
public class ConditionXmlWriter {

    private final XMLStreamWriter writer;
    private Map<String, String> attributes;

    public ConditionXmlWriter(XMLStreamWriter writer) {
        this.writer = writer;
    }

    public void writeCondition(Condition condition) throws XMLStreamException {
        attributes = new HashMap<String, String>();

        writeCondition_(condition);

        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            writer.writeAttribute(entry.getKey(), entry.getValue());
        }
    }

    protected void writeCondition_(Condition condition) throws XMLStreamException {
        switch (condition.getConditionType()) {
            /**
             * This condition checks exactly two conditions.
             * example:
             * <pre class="example">
             *   .part1:lang(fr)
             * </pre>
             */
            case SAC_AND_CONDITION:
                writeCombinatorCondition((CombinatorCondition) condition);
                break;

            /**
             * This condition checks one of two conditions.
             * @see org.w3c.css.sac.CombinatorCondition
             */
            case SAC_OR_CONDITION:
                System.out.println("condition = " + condition);
                writeCombinatorCondition((CombinatorCondition) condition);
                break;

            /**
             * This condition checks an attribute.
             * example:
             * <pre class="example">
             *   [simple]
             *   [restart="never"]
             * </pre>
             * @see AttributeCondition
             */
            case SAC_ATTRIBUTE_CONDITION:

                /**
                 * This condition checks an id attribute.
                 * example:
                 * <pre class="example">
                 *   #myId
                 * </pre>
                 * @see AttributeCondition
                 */
            case SAC_ID_CONDITION:

                /**
                 * This condition checks for a specified class.
                 * example:
                 * <pre class="example">
                 *   .example
                 * </pre>
                 * @see AttributeCondition
                 */
            case SAC_CLASS_CONDITION:
                writeAttributeCondition((AttributeCondition) condition, "");
                break;

            /**
             * This condition checks for the link pseudo class.
             * example:
             * <pre class="example">
             *   :link
             *   :visited
             *   :hover
             * </pre>
             * @see AttributeCondition
             */
            case SAC_PSEUDO_CLASS_CONDITION:
                writeAttributeCondition("pseudo-class", ((AttributeCondition) condition).getValue());
                break;

            /**
             * This condition checks for a value in a space-separated values in a
             * specified attribute
             * example:
             * <pre class="example">
             *   [values~="10"]
             * </pre>
             * @see AttributeCondition
             */
            case SAC_ONE_OF_ATTRIBUTE_CONDITION:
                writeAttributeCondition((AttributeCondition) condition, "~");
                break;

            /**
             * This condition checks if the value is in a hypen-separated list of values
             * in a specified attribute.
             * example:
             * <pre class="example">
             *   [languages|="fr"]
             * </pre>
             * @see AttributeCondition
             */
            case SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION:
                writeAttributeCondition((AttributeCondition) condition, "-");
                break;

            /**
             * This condition checks that a condition can't be applied to a node.
             * @see org.w3c.css.sac.NegativeCondition
             */
            case SAC_NEGATIVE_CONDITION:

                /**
                 * This condition checks a specified position.
                 * example:
                 * <pre class="example">
                 *   :first-child
                 * </pre>
                 * @see org.w3c.css.sac.PositionalCondition
                 */
            case SAC_POSITIONAL_CONDITION:

                /**
                 * This condition checks the language of the node.
                 * example:
                 * <pre class="example">
                 *   :lang(fr)
                 * </pre>
                 * @see org.w3c.css.sac.LangCondition
                 */
            case SAC_LANG_CONDITION:
                writeAttributeCondition("lang", ((LangCondition) condition).getLang());
                break;

            /**
             * This condition checks if a node is the only one in the node list.
             */
            case SAC_ONLY_CHILD_CONDITION:
                /**
                 * This condition checks if a node is the only one of his type.
                 */
            case SAC_ONLY_TYPE_CONDITION:
                /**
                 * This condition checks the content of a node.
                 * @see org.w3c.css.sac.ContentCondition
                 */
            case SAC_CONTENT_CONDITION:
            default:
                writer.writeAttribute("condition", condition.toString());
        }

    }

    private void writeAttributeCondition(String name, String value) throws XMLStreamException {
        String val = attributes.get(name);
        val = (val == null ? "" : val + ",") + (value == null ? "*" : value);

        attributes.put(name, val);
    }

    private void writeAttributeCondition(AttributeCondition condition, String prefix) throws XMLStreamException {
        String value = condition.getValue();
        writeAttributeCondition(String.valueOf(condition.getLocalName()), value == null ? "*" : prefix + value);
    }

    private void writeCombinatorCondition(CombinatorCondition condition) throws XMLStreamException {
        writeCondition_(condition.getFirstCondition());
        writeCondition_(condition.getSecondCondition());
    }
}
